Codeforces Round #842 (Div. 2)【A - E】

五十分钟四题 1A 下班,赛后补了下 E 。

比赛链接:https://codeforces.com/contest/1768

A. Greatest Convex

题解

x!+(x1)!=(x+1)(x1)!x! + (x - 1)! = (x + 1) \cdot (x - 1)!

x=k1x = k - 1 时,原式为 k(k2)!k \cdot (k - 2)! ,显然为 kk 的倍数。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#include <bits/stdc++.h>
using namespace std;

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);

int t;
cin >> t;

while (t--) {
int k;
cin >> k;

cout << (k - 1) << "\n";
}

return 0;
}

B. Quick Sort

题解

找以 1 开头的最长连续上升子序列,其他数都需要操作后移到尾部,次数即对 kk 取上整。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
#include <bits/stdc++.h>
using namespace std;

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);

int t;
cin >> t;

while (t--) {
int n, k;
cin >> n >> k;

vector<int> p(n);
for (int i = 0; i < n; i++) {
cin >> p[i];
}

int cnt = n, val = 1;
for (int i = 0; i < n; i++) {
if (p[i] == val) {
val++;
cnt--;
}
}
cout << (cnt + k - 1) / k << "\n";
}

return 0;
}

C. Elemental Decompress

题解

如果有数出现三次则无解。

否则,将第一次出现的数和第二次出现的数分别赋给 p,qp, q ,其余位置的数用 setupper_bound 贪心查找即可。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
#include <bits/stdc++.h>
using namespace std;

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);

int t;
cin >> t;

while (t--) {
int n;
cin >> n;

vector<int> a(n), cnt(n);
for (int i = 0; i < n; i++) {
cin >> a[i];
--a[i];
cnt[a[i]] += 1;
}

bool ok = true;
for (int i = 0; i < n; i++) {
if (cnt[i] >= 3) {
ok = false;
break;
}
}

if (not ok) {
cout << "NO" << "\n";
continue;
}

vector<int> p(n, -1), q(n, -1);
vector<bool> vis(n);
for (int i = 0; i < n; i++) {
if (not vis[a[i]]) {
p[i] = a[i];
vis[a[i]] = true;
}
}
for (int i = 0; i < n; i++) {
if (vis[a[i]] and p[i] == -1) {
q[i] = a[i];
}
}

auto fill_blank = [&](vector<int>& p, vector<int>& q) {
set<int> st;
for (int i = 0; i < n; i++) {
st.insert(i);
}
for (int i = 0; i < n; i++) {
st.erase(p[i]);
}
for (int i = 0; i < (int)p.size(); i++) {
if (p[i] == -1 and q[i] != -1) {
auto it = st.upper_bound(q[i]);
if (it == st.begin()) {
ok = false;
return;
}
--it;
p[i] = *it;
st.erase(it);
}
}
};

fill_blank(p, q);
fill_blank(q, p);

if (not ok) {
cout << "NO" << "\n";
} else {
cout << "YES" << "\n";
for (int i = 0; i < n; i++) {
cout << p[i] + 1 << " \n"[i == n - 1];
}
for (int i = 0; i < n; i++) {
cout << q[i] + 1 << " \n"[i == n - 1];
}
}

}

return 0;
}

D. Lucky Permutation

题解

只有一个逆序对即交换升序排列中的某对相邻值。

找出 pp 中的所有循环节,长度为 kk 的循环节需要 k1k - 1 次操作使得每个值回到自己对应的位置上。

若某个循环节里存在相邻值,则最后排为升序时可以减少一次操作来使这两个相邻值形成逆序。

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#include <bits/stdc++.h>
using namespace std;

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);

int t;
cin >> t;

while (t--) {
int n;
cin >> n;

vector<int> p(n);
for (int i = 0; i < n; i++) {
cin >> p[i];
--p[i];
}

int ans = 0;
bool ok = false;

vector<bool> vis(n);
for (int i = 0; i < n; i++) {
if (vis[i]) {
continue;
}
vector<int> v;
int j = i, res = -1;
while (not vis[j]) {
vis[j] = true;
v.push_back(p[j]);
j = p[j];
res += 1;
}
sort(v.begin(), v.end());
for (int j = 0; j + 1 < (int)v.size(); j++) {
if (v[j] + 1 == v[j + 1]) {
ok = true;
}
}
ans += res;
}

cout << (ans + (ok ? -1 : 1)) << "\n";
}

return 0;
}

E. Partial Sorting

题解

  • [1,n][1, n] 在后 nn 个位置或 [2n+1,3n][2n + 1, 3n] 在前 nn 个位置时需要操作 33
  • [1,n][1, n] 在前 2n2n 个位置或 [2n+1,3n][2n + 1, 3n] 在后 2n2n 个位置时需要操作 22
  • 当前 nn 个位置已有序或后 nn 个位置已有序时需要操作 11
  • 3n3n 个位置整体有序时不需要操作

由于不好单独计算某一操作次数的排列数,可以用容斥原理从低到高依次计算。

  • 操作 00 次, 3n3n 个位置整体有序, 11 种情况
  • 操作 1\le 1 次:
    • nn 个位置有序:共有 2n!2n! 种情况
    • nn 个位置有序:共有 2n!2n! 种情况
    • 二者交集,即前后 nn 个位置都有序,此时只有 [n+1,2n][n + 1, 2n] 在中间 nn 个位置排列,共有 n!n! 种情况
    • 所以共有 22n!n!2 \cdot 2n! - n! 种情况
  • 操作 2\le 2 次:
    • [1,n][1, n] 在前 2n2n 个位置,共有 C2nnn!2n!C_{2n}^{n} \cdot n! \cdot 2n! 种情况
    • [2n+1,3n][2n + 1, 3n] 在后 2n2n 个位置,共有 C2nnn!2n!C_{2n}^{n} \cdot n! \cdot 2n! 种情况
    • 二者交集,即 [1,n][1, n] 在前 2n2n 个位置,同时 [2n+1,3n][2n + 1, 3n] 在后 2n2n 个位置:
      • [1,n][1, n] 在前 nn 个位置中有 ii 个,那么在中间 nn 个位置中有 nin - i 个,共有 CniCnnin!C_{n}^{i} \cdot C_{n}^{n - i} \cdot n! 种情况
      • 此时 [2n+1,3n][2n + 1, 3n] 需要填补前 nn 个位置中 nin - i 个空缺,同时在后 2n(ni)2n - (n - i) 个位置中选取 ii 个位置,共有 C2n(ni)in!C_{2n - (n - i)}^{i} \cdot n! 种情况
      • 最后 [2n+1,3n][2n + 1, 3n] 填补后 2n2n 个位置中余下的 nn 个位置,共有 n!n! 种情况
      • 所以共有 2C2nnn!2n!i=0n(CniCnnin!)(C2n(ni)in!)(n!)2 \cdot C_{2n}^{n} \cdot n! \cdot 2n! - \sum \limits _{i = 0} ^{n} (C_{n}^{i} \cdot C_{n}^{n - i} \cdot n!) \cdot (C_{2n - (n - i)}^{i} \cdot n!) \cdot (n!) 种情况
  • 操作 3\le 3 次:
    • 共有 3n!3n! 种情况

依次相减得出操作次数等于 ii 次的情况数 cnticnt_i ,答案即 i=13icnti\sum \limits _{i = 1} ^{3} i \cdot cnt_i

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
#include <bits/stdc++.h>
using namespace std;

int MOD = 998244353;

int norm(int x) { if (x < 0) { x += MOD; } if (x >= MOD) { x -= MOD; } return x; }
template<class T> T binpow(T a, int b) { T res = 1; for (; b; b /= 2, a *= a) { if (b % 2) { res *= a; } } return res; }

struct Z {
int x;
Z(int x = 0) : x(norm(x)) {}
int val() const { return x; }
Z operator-() const { return Z(norm(MOD - x)); }
Z inv() const { assert(x != 0); return binpow(*this, MOD - 2); }
Z &operator*=(const Z &rhs) { x = 1LL * x * rhs.x % MOD; return *this; }
Z &operator+=(const Z &rhs) { x = norm(x + rhs.x); return *this; }
Z &operator-=(const Z &rhs) { x = norm(x - rhs.x); return *this; }
Z &operator/=(const Z &rhs) { return *this *= rhs.inv(); }
friend Z operator*(const Z &lhs, const Z &rhs) { Z res = lhs; res *= rhs; return res; }
friend Z operator+(const Z &lhs, const Z &rhs) { Z res = lhs; res += rhs; return res; }
friend Z operator-(const Z &lhs, const Z &rhs) { Z res = lhs; res -= rhs; return res; }
friend Z operator/(const Z &lhs, const Z &rhs) { Z res = lhs; res /= rhs; return res; }
};

struct Combination {
vector<Z> fac, inv;

Combination(int n) : fac(n), inv(n) {
fac[0] = 1;
for (int i = 1; i < n; i++) fac[i] = fac[i - 1] * i;
inv[n - 1] = fac[n - 1].inv();
for (int i = n - 2; i >= 0; i--) inv[i] = inv[i + 1] * (i + 1);
}

Z C(int n, int m){
if(m < 0 or m > n) return 0;
return fac[n] * inv[m] * inv[n - m];
}

Z A(int n, int m){
if(m < 0 or m > n) return 0;
return fac[n] * inv[n - m];
}
};

int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);

int n;
cin >> n >> MOD;

Combination C(3 * n + 1);

Z cnt0 = 1;

Z cnt1 = Z(2) * C.fac[2 * n] - C.fac[n] - cnt0;

Z cnt2 = Z(2) * C.C(2 * n, n) * C.fac[n] * C.fac[2 * n] - cnt1 - cnt0;
for (int i = 0; i <= n; i++) {
cnt2 -= C.C(n, i) * C.C(n, n - i) * C.C(2 * n - (n - i), i) * C.fac[n] * C.fac[n] * C.fac[n];
}

Z cnt3 = C.fac[3 * n] - cnt2 - cnt1 - cnt0;

cout << (cnt1 + 2 * cnt2 + 3 * cnt3).val() << "\n";

return 0;
}

Codeforces Round #842 (Div. 2)【A - E】

https://www.kanoon.cn/2023/01/06/CF 1768/

作者

Kanoon

发布于

2023-01-06

更新于

2023-01-11

许可协议

评论